package cc.shacocloud.mirage.utils.resource;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

/**
 * {@link Resource} 的抽象实现，提供一些便利的方法
 */
public abstract class AbstractResource implements Resource {
    
    private final Logger logger = LoggerFactory.getLogger(getClass());
    
    /**
     * 此实现检查是否可以打开文件，回退到是否可以打开输入流，这将涵盖目录和内容资源
     */
    @Override
    public boolean exists() {
        // 尝试文件存在
        if (isFile()) {
            try {
                return getFile().exists();
            } catch (IOException ignore) {
            
            }
        }
        // 通过是否可以打开流来检查
        try {
            getStream().close();
            return true;
        } catch (Throwable ignore) {
            return false;
        }
    }
    
    /**
     * 假定资源无法解析为 URL。
     */
    @Override
    public URL getURL() throws IOException {
        throw new FileNotFoundException(getDescription() + " 不能解析为 URL");
    }
    
    /**
     * 假定资源无法解析为绝对文件路径。
     */
    @Override
    public File getFile() throws IOException {
        throw new FileNotFoundException(getDescription() + " 不能解析为绝对文件路径");
    }
    
    
    /**
     * 此方法读取整个输入流以确定内容长度
     * <p>
     * 强烈建议使用更优化的实现来覆盖此方法，例如检查文件长度，或者如果流只能读取一次，则可能只返回 -1。
     *
     * @see #getStream()
     */
    @Override
    public long contentLength() throws IOException {
        InputStream is = getStream();
        try {
            long size = 0;
            byte[] buf = new byte[256];
            int read;
            while ((read = is.read(buf)) != -1) {
                size += read;
            }
            return size;
        } finally {
            try {
                is.close();
            } catch (IOException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("无法关闭 {} 的内容长度输入流 ", getDescription(), ex);
                }
            }
        }
    }
    
    /**
     * 此实现检查基础文件的时间戳（如果可用）
     *
     * @see #getFileForLastModifiedCheck()
     */
    @Override
    public long lastModified() throws IOException {
        File fileToCheck = getFileForLastModifiedCheck();
        long lastModified = fileToCheck.lastModified();
        if (lastModified == 0L && !fileToCheck.exists()) {
            throw new FileNotFoundException(getDescription() + " 无法在文件系统中解析以检查其上次修改的时间戳");
        }
        return lastModified;
    }
    
    /**
     * 确定用于时间戳检查的文件
     * <p>
     * 默认实现委托给 {@link #getFile()}.
     *
     * @return 用于时间戳检查的文件
     */
    @NotNull
    protected File getFileForLastModifiedCheck() throws IOException {
        return getFile();
    }
    
    /**
     * 此实现比较说明字符串
     *
     * @see #getDescription()
     */
    @Override
    public boolean equals(@Nullable Object other) {
        return (this == other || (other instanceof Resource &&
                ((Resource) other).getDescription().equals(getDescription())));
    }
    
    /**
     * 此实现返回说明的哈希代码
     *
     * @see #getDescription()
     */
    @Override
    public int hashCode() {
        return getDescription().hashCode();
    }
    
    /**
     * 此实现返回此资源的说明
     *
     * @see #getDescription()
     */
    @Override
    public String toString() {
        return getDescription();
    }
    
}
